package com.racer.mvc.dj.model;

import javax.sound.midi.*;
import java.util.*;
import com.racer.mvc.dj.view.*;

public class BeatModel implements BeatModelInterface,MetaEventListener{
    Sequencer sequencer;
    ArrayList<BeatObserver> beatObservers = new ArrayList<BeatObserver>();
    ArrayList<BPMObserver> bpmObservers = new ArrayList<BPMObserver>();
    int bpm=90;

    Sequence sequence;
    Track track;

    public void initialize(){
        setUpMidi();
        buildTrackAndStart();
    }

    public void on(){
        sequencer.start();
        setBPM(90);
    }

    public void off(){
        setBPM(0);
        sequencer.stop();
    }

    public void setBPM(int bpm){
        this.bpm=bpm;
        sequencer.setTempoInBPM(getBPM());
        notifyBPMObservers();
    }

    public int getBPM(){
        return bpm;
    }

    void beatEvent(){
        notifyBeatObservers();
    }

    public void registerObserver(BeatObserver o){
        beatObservers.add(o);
    }

    public void notifyBeatObservers(){
        for(int i=0;i<beatObservers.size();i++){
            BeatObserver o = (BeatObserver)beatObservers.get(i);
            o.updateBeat();
        }
    }

    public void removeObserver(BeatObserver o){
        int i = beatObservers.indexOf(o);
        if(i>=0)
            beatObservers.remove(i);
    }

    public void registerObserver(BPMObserver o){
        bpmObservers.add(o);
    }

    public void notifyBPMObservers(){
        for(int i=0;i<bpmObservers.size();i++){
            BPMObserver o = (BPMObserver)bpmObservers.get(i);
            o.updateBPM();
        }
    }

    public void removeObserver(BPMObserver o){
        int i = bpmObservers.indexOf(o);
        if(i>=0)
            bpmObservers.remove(i);
    }

    public void meta(MetaMessage message){
        System.out.println("meta message: type is "+message.getType());
        if(message.getType()==47){
            beatEvent();
            sequencer.start();
            setBPM(getBPM());
        }
    }

    public void setUpMidi(){
        try{
            sequencer=MidiSystem.getSequencer();
            sequencer.open();
            sequencer.addMetaEventListener(this);
            sequence=new Sequence(Sequence.PPQ,4);
            track=sequence.createTrack();
            sequencer.setTempoInBPM(getBPM());
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }

    public void buildTrackAndStart(){
        int[] trackList={35,0,46,0};
        sequence.deleteTrack(null);
        track = sequence.createTrack();

        makeTracks(trackList);
        track.add(makeEvent(192,9,1,0,4));
        try{
            sequencer.setSequence(sequence);
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }

    public void makeTracks(int[] list){
        for(int i=0;i<list.length;i++){
            int key=list[i];
            if(key!=0){
                track.add(makeEvent(144,9,key,100,i));
                track.add(makeEvent(128,9,key,100,i+1));
            }
        }
    }

    public MidiEvent makeEvent(int comd,int chan,int one,int two,int tick){
        MidiEvent event = null;
        try{
            ShortMessage a = new ShortMessage();
            a.setMessage(comd,chan,one,two);
            event=new MidiEvent(a,tick);
        }
        catch(Exception e){
            e.printStackTrace();
        }
        return event;
    }
}
